﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using ConceptsParser.SyntaxTree;
using Compiler.TableOfSymbols;
using Compiler.CompilerAPI;
using Compiler.TableOfSymbols.Concepts;
using Compiler.CompilerAPI.Contexts;

namespace Compiler.SyntaxTreeAnalysis
{
    public class GeneralSemanticVisitor : ISyntaxTreeVisitor
    {
        private CompilationEnvironment environment;

        public GeneralSemanticVisitor(CompilationEnvironment environment)
        {
            if (environment == null)
                throw new ArgumentNullException("environment can not be null");
            this.environment = environment;
        }

        public void Visit(ConceptsSyntaxTreeNode node)
        {
            node.AcceptVisitor(this);
        }

        public void Visit(ProgramNode node)
        {
            Visit(node.UsingSection);
            Visit(node.ConceptsSection);
        }

        public void Visit(ProgramSectionNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(DeclarationNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(DirectiveNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(DefinitionNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(QualifiedNamespaceNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(TypeQualifierNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(IDTypeQualifierNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(ConceptReferenceQualifierNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(TypeArgumentNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(SimpleTypeArgumentNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(GenericTypeArgumentNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(UsingSectionNode node)
        {
            foreach (UsingDirectiveNode directive in node.Directives)
                Visit(directive);
        }

        public void Visit(UsingDirectiveNode node)
        {
            string namespaceName = node.QualifiedNamespace.ToString();
            if (environment.MainContext.NamespaceIsAvailable(namespaceName))
                environment.MainContext.UseNamespace(namespaceName);
            else
                environment.ErrorManager.AddSemanticError(new SemanticError(
                    String.Format("Неизвестное пространство имён {0}", namespaceName), 
                    node.Location));
        }

        public void Visit(ConceptsSectionNode node)
        {
            foreach (ConceptDefinitionNode concept in node.Concepts)
                Visit(concept);
        }

        public void Visit(ConceptDefinitionNode node)
        {
            if (environment.MainContext.ContainsDefinedName(node.Signature.Name))
            {
                environment.ErrorManager.AddSemanticError(new SemanticError(
                    String.Format(
                        "Нельзя определить концепт {0}. В этой области видимости уже определена единица с таким именем",
                        node.Signature.Name), node.Location));
                return;
            }
            ConceptDefinition conceptData = new ConceptDefinition(node.Signature.Name);
            environment.MainContext.AddNewDefinition(conceptData.Name, conceptData);
            ConceptContext conceptContext = new ConceptContext(environment.TypesBijectiveMap, conceptData);
            environment.WorkingContext.Push(conceptContext);
            // concept definition analysis
            ConceptVisitor conceptVisitor = new ConceptVisitor(environment, conceptContext);
            conceptVisitor.Visit(node);
        }

        public void Visit(ConceptSignatureNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(ConceptBodyNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(ConceptReferenceNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(InnerConceptDeclarationNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(InnerConceptRequirementNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(InnerConceptAliasNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(NestedConceptRequirementNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(AssociatedTypeDeclarationNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(ConstraintOnTypeArgNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(TypeRequirementNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(ConceptAliasNode node)
        {
            throw new InvalidOperationException();
        }

        public void Visit(TypeAliasNode node)
        {
            throw new InvalidOperationException();
        }
    }
}
